<template>
  <!-- 定义一个全屏的 div 容器，背景色设置为透明 -->
  <div
    ref="container"
    h-full
    style="background-color: transparent"
    w-full
  ></div>
</template>

<script lang="ts" setup>
// 导入 Three.js 库和 Vue 3 的 Composition API 相关函数
import * as THREE from "three";
import { onMounted, onUnmounted, ref } from "vue";

// 定义一个类型，用于存储粒子的最大高度和最小高度
type ParticleHeightArray = {
  maxHeight: number;
  minHeight: number;
}[];

// 创建对容器元素的引用，以及 Three.js 场景中的相机、场景和渲染器的引用
const container = ref<HTMLDivElement | null>(null);
let camera: THREE.PerspectiveCamera;
let scene: THREE.Scene;
let renderer: THREE.WebGLRenderer;
let particles: THREE.Points;
let lines: THREE.LineSegments;
// 初始化一些变量，包括鼠标位置、计数器和随机高度时间
let mouseX = 0;
let count = 0;
let randomHeightTime = 0;

// 定义粒子的分离度和数量
const SEPARATION = 500;
const AMOUNTX = 50;
const AMOUNTY = 50;

// 在 setup() 函数之外定义并初始化 particleHeightArray
//初始化粒子高度数组，每个粒子都有一个随机的最大高度和最小高度
const particleHeightArray: ParticleHeightArray = [];
for (let ix = 0; ix < AMOUNTX; ix++) {
  for (let iy = 0; iy < AMOUNTY; iy++) {
    const maxHeight = Math.random() * 500; // 最大高度
    const minHeight = Math.random() * -500; // 最小高度
    particleHeightArray.push({ maxHeight, minHeight });
  }
}

// 定义顶点着色器和片段着色器的代码
// 顶点着色器代码
const vertexShaderCode = `
      attribute float scale;
      void main() {
        vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
        gl_PointSize = scale * ( 300.0 / - mvPosition.z );
        gl_Position = projectionMatrix * mvPosition;
      }
    `;
// 片段着色器代码
const fragmentShaderCode = `
      uniform vec3 color;
      void main() {
        if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
        gl_FragColor = vec4( color, 1.0 );
      }
    `;

 // 组件挂载后初始化场景、相机、渲染器等，并开始动画循环
onMounted(() => {
  init();
  animate();
  // 监听窗口大小变化和鼠标移动事件
  window.addEventListener("resize", onWindowResize);
  if (container.value) {
    document.addEventListener("mousemove", onMouseMove);
  }
});

// 组件卸载前移除事件监听器
onUnmounted(() => {
  window.removeEventListener("resize", onWindowResize);
  if (container.value) {
    document.removeEventListener("mousemove", onMouseMove);
  }
});

// 初始化 Three.js 场景
function init() {
  if (!container.value) return;

  camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    1,
    10000
  );
  camera.position.set(1000, 1000, 1000); // 将摄像机位置设置为俯视
  camera.lookAt(0, 0, 100); // 摄像机看向场景中心

  scene = new THREE.Scene();

  const numParticles = AMOUNTX * AMOUNTY;

  const positions = new Float32Array(numParticles * 3);
  const scales = new Float32Array(numParticles);

  let i = 0,
    j = 0;

  for (let ix = 0; ix < AMOUNTX; ix++) {
    for (let iy = 0; iy < AMOUNTY; iy++) {
      positions[i] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2;
      positions[i + 1] = Math.random() * 100; // 使用随机数生成粒子的高度
      positions[i + 2] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2;

      scales[j] = 50;

      i += 3;
      j++;
    }
  }

  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute("scale", new THREE.BufferAttribute(scales, 1));

  const material = new THREE.ShaderMaterial({
    uniforms: {
      color: { value: new THREE.Color(0xf5f7f8) },
    },
    vertexShader: vertexShaderCode,
    fragmentShader: fragmentShaderCode,
  });

  particles = new THREE.Points(geometry, material);
  scene.add(particles);

  // 创建连线
  const lineGeometry = new THREE.BufferGeometry();
  const numLines = (AMOUNTX - 1) * AMOUNTY + (AMOUNTY - 1) * AMOUNTX;
  const linePositions = new Float32Array(numLines * 2 * 3);

  let k = 0;

  // 纵向线
  for (let ix = 0; ix < AMOUNTX; ix++) {
    for (let iy = 0; iy < AMOUNTY - 1; iy++) {
      linePositions[k++] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2;
      linePositions[k++] = 0;
      linePositions[k++] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2;

      linePositions[k++] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2;
      linePositions[k++] = 0;
      linePositions[k++] = (iy + 1) * SEPARATION - (AMOUNTY * SEPARATION) / 2;
    }
  }

  // 横向线
  for (let ix = 0; ix < AMOUNTX - 1; ix++) {
    for (let iy = 0; iy < AMOUNTY; iy++) {
      linePositions[k++] = ix * SEPARATION - (AMOUNTX * SEPARATION) / 2;
      linePositions[k++] = 0;
      linePositions[k++] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2;

      linePositions[k++] = (ix + 1) * SEPARATION - (AMOUNTX * SEPARATION) / 2;
      linePositions[k++] = 0;
      linePositions[k++] = iy * SEPARATION - (AMOUNTY * SEPARATION) / 2;
    }
  }

  lineGeometry.setAttribute(
    "position",
    new THREE.BufferAttribute(linePositions, 3)
  );
  const lineMaterial = new THREE.LineBasicMaterial({ color: 0x1a3866 });
  lines = new THREE.LineSegments(lineGeometry, lineMaterial);
  scene.add(lines);

  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setClearColor(new THREE.Color(0x000000), 0);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  // renderer.setClearColor(0x00112b); // 设置背景色

  if (container.value) {
    container.value.appendChild(renderer.domElement);
  }
}

// 窗口大小变化时更新相机和渲染器
function onWindowResize() {
  if (!container.value) return;

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

// 鼠标移动时更新鼠标位置变量
function onMouseMove(event: MouseEvent) {
  mouseX = (event.clientX - window.innerWidth / 2) * 1;
}

// 动画循环函数，不断请求下一帧动画
function animate() {
  requestAnimationFrame(animate);
  render();
}

// 渲染函数，更新粒子和线条的位置，并渲染场景
function render() {
  camera.position.x += (mouseX - camera.position.x) * 0.05;
  camera.lookAt(scene.position);
  const positions = particles.geometry.attributes.position.array;

  let i = 0;
  for (let ix = 0; ix < AMOUNTX; ix++) {
    for (let iy = 0; iy < AMOUNTY; iy++) {
      const particleIndex = ix * AMOUNTY + iy;
      const { maxHeight, minHeight } = particleHeightArray[particleIndex];

      // 使用两个 sin 函数叠加，一个控制波浪效果，一个控制随机最大值
      const waveHeight = Math.sin((iy + count) * 0.1) * 30;
      const randomMaxHeight = (Math.sin((ix + count) * 0.5) + 1) * 50;

      // 将随机最大值和波浪高度叠加，并在最小值和最大值之间进行限制
      const height = Math.min(
        maxHeight,
        Math.max(minHeight, minHeight + waveHeight + randomMaxHeight)
      );

      // 将计算后的高度应用到粒子的位置
      positions[i + 1] = height;
      i += 3;
    }
  }

  particles.geometry.attributes.position.needsUpdate = true;
  particles.geometry.attributes.scale.needsUpdate = true;

  // 更新连线的位置
  const linePositions = lines.geometry.attributes.position.array;
  let k = 0;

  // 更新纵向线
  for (let ix = 0; ix < AMOUNTX; ix++) {
    for (let iy = 0; iy < AMOUNTY - 1; iy++) {
      linePositions[k + 1] = positions[(ix * AMOUNTX + iy) * 3 + 1];
      linePositions[k + 4] = positions[(ix * AMOUNTX + iy + 1) * 3 + 1];
      k += 6;
    }
  }

  // 更新横向线
  for (let ix = 0; ix < AMOUNTX - 1; ix++) {
    for (let iy = 0; iy < AMOUNTY; iy++) {
      linePositions[k + 1] = positions[(ix * AMOUNTX + iy) * 3 + 1];
      linePositions[k + 4] = positions[((ix + 1) * AMOUNTX + iy) * 3 + 1];
      k += 6;
    }
  }

  lines.geometry.attributes.position.needsUpdate = true;

  renderer.render(scene, camera);

  count += 0.1;
}
</script>
